// Editor.cs
//

using System;
using System.Collections.Generic;
using jQueryApi;
using System.Diagnostics;
using System.Xml;

namespace TuringMachine
{

   public class CellEditor : Div
   {
      StateCell[] m_movCells;
      StateCell[] m_altMovCells;
      List<StateCell> m_charCells;
      List<StateCell> m_opCells;
      StateCell m_selection = null;
      StateBoard m_board;
      Label m_toolTip;
      public event EventHandler VisibilityChanges;
      bool m_bSelectorShown = false;
      string[] m_opToolTips = new string[]{
         "Check if the current tape has a given value",
         "Write a given value to the current tape",
         "Move the current tape forward",
         "Move the current tape backwards",
         "Make the red tape the current tape",
         "Make the white tape the current tape",
         "Compare the values of the two tapes",
         "Accept and end program",
         "Swap the value of the current tape",
         "Write the value of the other tape to the current tape",
         "Jump to the given value function or return from it",
         "Insert a new cell in the current tape",
         "Remove the current cell from the tape"
      };

      string[] m_charName = new string[]{
         "zero","one","red dot","green dot"
      };


      public CellEditor(StateBoard parent, string id) :
         base(parent.GetCell(0, 0).pObject, id)
      {
         m_board = parent;
         m_board.GetCell(0, 0).pObject.After("<div id=\"" + id + "\"></div>");
         pObject = jQuery.Select("#" + id);
         pObject.CSS("display", "none");
         pObject.Width(StateBoard.DISTANCE_CELL * 11);
         pObject.Height(StateBoard.DISTANCE_CELL * 6);
         pObject.CSS("border", "1px black solid");
         pObject.CSS("background-color", "#ffffe0");
         pObject.CSS("z-index", "100");
         pObject.CSS("position", "absolute");

         m_toolTip = new Label(pObject, "tooltip");

         m_toolTip.pObject.CSS("position", "absolute");
         m_toolTip.pVisible = false;
         m_toolTip.pObject.CSS("width", "200px");
         m_toolTip.pObject.CSS("height", "70px");
         m_toolTip.pObject.CSS("padding", "5px");
         m_toolTip.pObject.CSS("background-color", "#ffff00");
         m_toolTip.pObject.CSS("border", "1px solid black");

         m_selection = new StateCell(pObject);
         m_selection.pPosX = (int)(4.5 * StateBoard.DISTANCE_CELL) + 2;
         m_selection.pPosY = 2;
         m_selection.pBkgNormalColor = 6;
         m_selection.OnClick += new CellEventHandler(m_selection_OnClick);

         m_movCells = new StateCell[9];
         m_altMovCells = new StateCell[8];

         int[] dirArray = { 7, 0, 1, 6, -1, 2, 5, 4, 3 };
         int dirArrayPos = 0;
         int dirAltArrayPos = 0;

         int posX_Movement = (int)(4 * StateBoard.DISTANCE_CELL) + 1;
         int posY_Movement = (int)(1.5 * StateBoard.DISTANCE_CELL) + 1;
         int posX_AltMovement = (int)(7.5 * StateBoard.DISTANCE_CELL) + 1;
         int posY_AltMovement = (int)(1.5 * StateBoard.DISTANCE_CELL) + 1;
         int posX_Character = (int)(5.25 * StateBoard.DISTANCE_CELL) + 1;
         int posY_Character = (int)(4.75 * StateBoard.DISTANCE_CELL) + 1;
         int posX_Operation = (int)(0.5 * StateBoard.DISTANCE_CELL) + 1;
         int posY_Operation = (int)(1.5 * StateBoard.DISTANCE_CELL) + 1;

         for (int j = -1; j < 2; j++)
            for (int i = -1; i < 2; i++)
            {
               m_movCells[dirArrayPos] = new StateCell(pObject);
               m_movCells[dirArrayPos].pPosX = posX_Movement + (int)((i + 1) * StateBoard.DISTANCE_CELL) + 1;
               m_movCells[dirArrayPos].pPosY = posY_Movement + (int)((j + 1) * StateBoard.DISTANCE_CELL + 1);
               m_movCells[dirArrayPos].pMovement = dirArray[dirArrayPos];
               m_movCells[dirArrayPos].pBkgNormalColor = 7;

               m_movCells[dirArrayPos].OnMouseEnter += new CellEventHandler(CellEditor_OnMouseEnter);
               m_movCells[dirArrayPos].OnMouseLeave += new CellEventHandler(CellEditor_OnMouseLeave);
               m_movCells[dirArrayPos].OnMouseMove += new CellEventHandlerWithEvent(CellEditor_OnMouseMove);


               m_movCells[dirArrayPos].OnClick += delegate(StateCell clickedCell, jQueryEvent ev)
               {
                  int newDir = clickedCell.pMovement;
                  if (newDir == m_selection.pMovement && newDir != -1 && m_board.m_abilities.pDoubleMovementEnabled)
                     m_selection.pMovement = (newDir + 8) % 16;
                  else
                     m_selection.pMovement = newDir;

                  CheckMissingInfo();
               };

               if (j != 0 || i != 0)
               {
                  m_altMovCells[dirAltArrayPos] = new StateCell(pObject);
                  m_altMovCells[dirAltArrayPos].pPosX = posX_AltMovement + (int)((i + 1) * StateBoard.DISTANCE_CELL) + 1;
                  m_altMovCells[dirAltArrayPos].pPosY = posY_AltMovement + (int)((j + 1) * StateBoard.DISTANCE_CELL + 1);
                  m_altMovCells[dirAltArrayPos].pAltMovement = dirArray[dirArrayPos];
                  m_altMovCells[dirAltArrayPos].pBkgNormalColor = 7;

                  m_altMovCells[dirAltArrayPos].OnMouseEnter += new CellEventHandler(CellEditor_OnMouseEnter);
                  m_altMovCells[dirAltArrayPos].OnMouseLeave += new CellEventHandler(CellEditor_OnMouseLeave);
                  m_altMovCells[dirAltArrayPos].OnClick += delegate(StateCell clickedCell, jQueryEvent ev)
                  {
                     m_selection.pAltMovement = clickedCell.pAltMovement;
                     CheckMissingInfo();
                  };
                  dirAltArrayPos++;
               }

               dirArrayPos++;
            }

         m_charCells = new List<StateCell>();
         for (int charNum = 0; charNum < 4; charNum++)
         {
            if (charNum == 2 && !m_board.m_abilities.pRedDotEnabled) continue;
            StateCell cell = new StateCell(pObject);

            cell.pPosX = posX_Character + m_charCells.Count * StateBoard.DISTANCE_CELL;
            cell.pPosY = posY_Character;
            cell.pCharacter = charNum;
            cell.pBkgNormalColor = 7;

            cell.OnMouseEnter += new CellEventHandler(CellEditor_OnMouseEnter);
            cell.OnMouseLeave += new CellEventHandler(CellEditor_OnMouseLeave);

            cell.OnClick += delegate(StateCell clickedCell, jQueryEvent ev)
            {
               m_selection.pCharacter = clickedCell.pCharacter;
               CheckMissingInfo();
            };
            m_charCells.Add(cell);
         }

         m_opCells = new List<StateCell>();
         int pos = 0;
         for (int op = 0; op < StateBoard.MAX_OP + 1; op++)
         {
            if (op == StateBoard.OP_FUNCTION && !m_board.m_abilities.pOpFunctionEnabled) continue;
            if ((op == StateBoard.OP_RED_TAPE || op == StateBoard.OP_WHITE_TAPE) &&
               !m_board.m_abilities.pTwoTapesEnabled) continue;
            if (op == StateBoard.OP_FLIP_BIT && !m_board.m_abilities.pOpSwapValueEnabled) continue;
            if (op == StateBoard.OP_WRITE_VAL_TAPE && !m_board.m_abilities.pOpWriteValueFromOtherEnabled) continue;
            if (op == StateBoard.OP_COMPARE_TAPES && !m_board.m_abilities.pOpCompareEnabled) continue;
            if (op == StateBoard.OP_INSERTCELL && !m_board.m_abilities.pOpInsertEnabled) continue;
            if (op == StateBoard.OP_REMOVECELL && !m_board.m_abilities.pOpRemoveEnabled) continue;
            StateCell opCell = new StateCell(pObject);
            m_opCells.Add(opCell);

            if (op == StateBoard.MAX_OP)
               opCell.pOperation = -1;
            else
               opCell.pOperation = op;

            int posX = pos % 3;
            int posY = (int)(pos / 3);
            opCell.pPosX = posX_Operation + (int)(posX * StateBoard.DISTANCE_CELL);
            opCell.pPosY = posY_Operation + (int)(posY * StateBoard.DISTANCE_CELL);

            opCell.OnMouseEnter += new CellEventHandler(CellEditor_OnMouseEnter);
            opCell.OnMouseLeave += new CellEventHandler(CellEditor_OnMouseLeave);
            opCell.OnMouseMove += new CellEventHandlerWithEvent(CellEditor_OnMouseMove);
            opCell.pBkgNormalColor = 7;

            opCell.OnClick += delegate(StateCell clickedCell, jQueryEvent ev)
            {
               m_selection.pOperation = clickedCell.pOperation;
               CheckMissingInfo();
            };
            pos++;
         }


         pObject.MouseLeave(delegate
         {
            CheckMissingInfo();
         });
         pObject.Children().CSS("z-index", "100");
         m_toolTip.pObject.CSS("z-index", "110");
      }

      void CellEditor_OnMouseMove(StateCell sender, jQueryEvent ev)
      {
         if (m_toolTip.pVisible)
         {
            m_toolTip.pPosX = GetOffsetX(ev);
            m_toolTip.pPosY = GetOffsetY(ev);
         }
      }

      void CellEditor_OnMouseLeave(StateCell cell, jQueryEvent ev)
      {
         cell.pbHovered = false;
         m_toolTip.pVisible = false;
      }

      void CellEditor_OnMouseEnter(StateCell cell, jQueryEvent ev)
      {
         if (cell.pOperation != -1)
         {
            m_toolTip.pVisible = true;
            m_toolTip.pText = m_opToolTips[cell.pOperation];
            if (cell.pCharacter != -1)
               m_toolTip.pText = m_toolTip.pText.Replace("given value", m_charName[cell.pCharacter]);
         }
         if (cell.pMovement != -1)
         {
            m_toolTip.pVisible = true;
            if (m_selection.pOperation == 10)
               m_toolTip.pText = "Choose where the function begins or no movement to return to caller";
            else
               m_toolTip.pText = "Choose where the execution point moves to, after the operation completes";
         }
         if (cell.pAltMovement != -1)
         {
            m_toolTip.pVisible = true;
            m_toolTip.pText = "Choose where the execution point moves to, if the condition is met";
         }
         if (m_toolTip.pVisible)
         {
            m_toolTip.pPosX = GetOffsetX(ev);
            m_toolTip.pPosY = GetOffsetY(ev);
         }
         cell.pbHovered = true;
      }

      private int GetOffsetX(jQueryEvent ev)
      {
         return ev.PageX - pObject.Position().Left;
      }

      private int GetOffsetY(jQueryEvent ev)
      {
         return ev.PageY - pObject.Position().Top;
      }

      private void SetVisibility()
      {
         bool bShowChars = CellNeedsCharValue(m_selection);
         bool bShowAltMovement = CellNeedsAltMovValue(m_selection);
         bool bShowMovement = CellAllowsMovValue(m_selection);

         for (int dirIdx = 0; dirIdx < 9; dirIdx++)
         {
            bool bVisible = bShowMovement && (!bShowAltMovement || m_movCells[dirIdx].pMovement != m_selection.pAltMovement);
            if (m_movCells[dirIdx].pMovement == -1)
               bVisible = !CellNeedsMovValue(m_selection);
            m_movCells[dirIdx].pVisible = bVisible;
            m_movCells[dirIdx].SetThickBorder(m_selection.pMovement == m_movCells[dirIdx].pMovement);
         }

         for (int dirIdx = 0; dirIdx < 8; dirIdx++)
         {
            bool bVisible = bShowAltMovement && m_altMovCells[dirIdx].pAltMovement != m_selection.pMovement;
            m_altMovCells[dirIdx].pVisible = bVisible;
            m_altMovCells[dirIdx].SetThickBorder(m_selection.pAltMovement == m_altMovCells[dirIdx].pAltMovement);
         }

         foreach (StateCell charCell in m_charCells)
         {
            bool bVisible = bShowChars;
            charCell.pOperation = m_selection.pOperation;
            charCell.pVisible = bShowChars;
            charCell.SetThickBorder(m_selection.pCharacter == charCell.pCharacter);
         }

         foreach (StateCell opCell in m_opCells)
         {
            opCell.SetThickBorder(opCell.pOperation == m_selection.pOperation);
         }
      }

      private void CheckMissingInfo()
      {
         bool bOkToClose = true;
         bool bShowAltMovement = CellNeedsAltMovValue(m_selection);
         if (CellNeedsMovValue(m_selection) && m_selection.pMovement == -1)
            bOkToClose = false;
         else if (!CellAllowsMovValue(m_selection))
            m_selection.pMovement = -1;

         if (bShowAltMovement)
         {
            if (m_selection.pAltMovement == -1)
               bOkToClose = false;
         }
         else
         {
            m_selection.pAltMovement = -1;
         }

         if (CellNeedsCharValue(m_selection))
         {
            if (m_selection.pCharacter == -1)
               bOkToClose = false;
         }
         else
            m_selection.pCharacter = -1;

         if (m_board.pSelectedCell != null)
            m_board.pSelectedCell.GetDataFrom(m_selection);

         if (!bOkToClose)
            SetVisibility();
         else
            pDropDown = false;
      }

      void m_selection_OnClick(StateCell sender, jQueryEvent ev)
      {
         CheckMissingInfo();
      }

      bool CellAllowsMovValue(StateCell cell)
      {
         return cell.pOperation != 7;
      }

      bool CellNeedsMovValue(StateCell cell)
      {
         return cell.pOperation != 7 && cell.pOperation != -1 && cell.pOperation != 10;
      }

      bool CellNeedsAltMovValue(StateCell cell)
      {
         return cell.pOperation == 0 || cell.pOperation == 6;
      }


      bool CellNeedsCharValue(StateCell cell)
      {
         return cell.pOperation == 0 || cell.pOperation == 1 || cell.pOperation == 10;
      }

      bool CellHasAllInformation(StateCell cell)
      {
         return CellNeedsCharValue(cell) == (cell.pCharacter != -1) &&
            CellAllowsMovValue(cell) == (cell.pMovement != -1);
      }

      public bool pDropDown
      {
         get { return m_bSelectorShown; }
         set
         {
            if (m_bSelectorShown != value)
            {
               if (value)
               {
                  pObject.SlideDown(100);

               }
               else
               {
                  pObject.SlideUp(100);
                  m_board.pSelectedCell.pbHovered = false;
               }
               m_bSelectorShown = value;
               if (VisibilityChanges != null)
                  VisibilityChanges(this, null);
            }

         }
      }



      public void EditCell(StateCell cell)
      {
         m_selection.GetDataFrom(cell);
         SetVisibility();

         int positionSelection = (int)(StateBoard.DISTANCE_CELL * 4.5);
         if (cell.pPosX - positionSelection < 0)
            positionSelection = cell.pPosX;

         if (cell.pPosX - positionSelection + pObject.GetWidth() > StateBoard.DISTANCE_CELL * m_board.BOARD_SIZE_X)
            positionSelection = pObject.GetWidth() + cell.pPosX - StateBoard.DISTANCE_CELL * m_board.BOARD_SIZE_X;

         m_selection.pPosX = positionSelection;
         pPosX = (int)(cell.pPosX - positionSelection - 1);
         pPosY = cell.pPosY - 3;

         pDropDown = true;
      }
   }

}
